package com.zhiche.wms.service.inbound.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.toolkit.CollectionUtils;
import com.google.common.collect.Lists;
import com.zhiche.wms.configuration.MyConfigurationProperties;
import com.zhiche.wms.core.supports.BaseException;
import com.zhiche.wms.core.supports.enums.InterfaceAddrEnum;
import com.zhiche.wms.core.supports.enums.InterfaceEventEnum;
import com.zhiche.wms.core.supports.enums.TableStatusEnum;
import com.zhiche.wms.core.utils.HttpClientUtil;
import com.zhiche.wms.core.utils.HttpRequestUtil;
import com.zhiche.wms.core.utils.SnowFlakeId;
import com.zhiche.wms.domain.mapper.inbound.FactoryPrepareInboundMapper;
import com.zhiche.wms.domain.mapper.inbound.InboundNoticeHeaderMapper;
import com.zhiche.wms.domain.mapper.inbound.InboundPutawayHeaderMapper;
import com.zhiche.wms.domain.mapper.log.ItfExplogLineMapper;
import com.zhiche.wms.domain.mapper.otm.OtmOrderReleaseMapper;
import com.zhiche.wms.domain.mapper.otm.OtmShipmentMapper;
import com.zhiche.wms.domain.mapper.stock.SkuMapper;
import com.zhiche.wms.domain.mapper.stock.StockMapper;
import com.zhiche.wms.domain.model.base.StoreLocation;
import com.zhiche.wms.domain.model.base.Storehouse;
import com.zhiche.wms.domain.model.inbound.*;
import com.zhiche.wms.domain.model.log.ItfExplogHeader;
import com.zhiche.wms.domain.model.log.ItfExplogLine;
import com.zhiche.wms.domain.model.movement.MovementHeader;
import com.zhiche.wms.domain.model.movement.MovementLine;
import com.zhiche.wms.domain.model.opbaas.ExceptionRegister;
import com.zhiche.wms.domain.model.opbaas.OpTask;
import com.zhiche.wms.domain.model.opbaas.StatusLog;
import com.zhiche.wms.domain.model.otm.OtmOrderRelease;
import com.zhiche.wms.domain.model.otm.OtmShipment;
import com.zhiche.wms.domain.model.stock.Sku;
import com.zhiche.wms.domain.model.stock.Stock;
import com.zhiche.wms.domain.model.sys.User;
import com.zhiche.wms.service.base.IBusinessDocNumberService;
import com.zhiche.wms.service.base.IStoreLocationService;
import com.zhiche.wms.service.base.IStorehouseService;
import com.zhiche.wms.service.common.IntegrationService;
import com.zhiche.wms.service.constant.MovementType;
import com.zhiche.wms.service.constant.Status;
import com.zhiche.wms.service.dto.OTMEvent;
import com.zhiche.wms.service.inbound.*;
import com.zhiche.wms.service.log.IItfExplogHeaderService;
import com.zhiche.wms.service.log.IItfExplogLineService;
import com.zhiche.wms.service.movement.IMovementHeaderService;
import com.zhiche.wms.service.opbaas.IExceptionRegisterService;
import com.zhiche.wms.service.opbaas.IOrderReleaseService;
import com.zhiche.wms.service.opbaas.IStatusLogService;
import com.zhiche.wms.service.opbaas.ITaskService;
import com.zhiche.wms.service.otm.IOtmOrderReleaseService;
import com.zhiche.wms.service.otm.IOtmShipmentService;
import com.zhiche.wms.service.sys.IUserService;
import com.zhiche.wms.service.utils.BusinessNodeExport;
import com.zhiche.wms.service.utils.CommonFields;
import com.zhiche.wms.service.utils.CommonMethod;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * <p>
 * 入库单头 服务实现类
 * </p>
 *
 * @author qichao
 * @since 2018-06-08
 */
@Service
public class InboundPutawayHeaderServiceImpl extends ServiceImpl<InboundPutawayHeaderMapper, InboundPutawayHeader> implements IInboundPutawayHeaderService {

    @Autowired
    private IInboundNoticeHeaderService inboundNoticeHeaderService;
    @Autowired
    private IInboundNoticeLineService inboundNoticeLineService;
    @Autowired
    private IStoreLocationService storeLocationService;
    @Autowired
    private IBusinessDocNumberService businessDocNumberService;
    @Autowired
    private IMovementHeaderService movementHeaderService;
    @Autowired
    private IInboundPutawayLineService inboundPutawayLineService;
    @Autowired
    private SnowFlakeId snowFlakeId;
    @Autowired
    private IUserService userService;
    @Autowired
    private IntegrationService integrationService;
    @Autowired
    private IOtmOrderReleaseService releaseService;
    @Autowired
    private BusinessNodeExport nodeExport;
    @Autowired
    private ITaskService taskService;
    @Autowired
    private IInboundInspectLineService inspectLineService;
    @Autowired
    private IInboundInspectHeaderService inspectHeaderService;
    @Autowired
    private IStatusLogService statusLogService;
    @Autowired
    private OtmOrderReleaseMapper otmOrderReleaseMapper;
    @Autowired
    private MyConfigurationProperties properties;
    @Autowired
    private IExceptionRegisterService registerService;
    @Autowired
    private IItfExplogHeaderService explogHeaderService;
    @Autowired
    private IStorehouseService storehouseService;
    @Autowired
    private IItfExplogLineService logLineService;
    @Autowired
    private SkuMapper skuMapper;
    @Autowired
    private StockMapper stockMapper;
    @Autowired
    private ItfExplogLineMapper itfExplogLineMapper;
    @Autowired
    private FactoryPrepareInboundMapper factoryPrepareInboundMapper;
    @Autowired
    private OtmShipmentMapper otmShipmentMapper;
    @Autowired
    private InboundNoticeHeaderMapper inboundNoticeHeaderMapper;
    @Autowired
    private IOtmShipmentService iOtmShipmentService;
    @Autowired
    private IOrderReleaseService iOrderReleaseService;

    private static final Logger LOGGER = LoggerFactory.getLogger(InboundPutawayHeaderServiceImpl.class);

    @Override
    @Transactional(rollbackFor = Exception.class)
    public synchronized InboundPutawayHeader updateByNoticeLineId (Long noticeLineId, String inboundType, String genMethod, Long areaId) {
        LOGGER.info("入库通知单noticeLineId : {}进行入库操作", noticeLineId);
        //获取入库通知单明细
        InboundNoticeLine inboundNoticeLine = inboundNoticeLineService.selectById(noticeLineId);
        if (Objects.isNull(inboundNoticeLine)) {
            throw new BaseException("入库通知单明细不存在！");
        }

        //南昌提车段入库车辆进行发运操作
        inboundDispatch(inboundNoticeLine.getLineSourceKey(), inboundNoticeLine.getLotNo1());

        if (inboundNoticeLine.getStatus().equals(Status.Inbound.ALL)) {
            throw new BaseException("已入库不能重复入库！");
        }
        if (inboundNoticeLine.getStatus().equals(Status.Inbound.CANCEL)) {
            throw new BaseException("入库已取消，不能入库！");
        }

        //得到车型大中小属性 hgy 20200805
        String vehicleSize = "";
        vehicleSize = inboundNoticeLine.getLotNo5();
        //获取入库通知单头
        InboundNoticeHeader inboundNoticeHeader = inboundNoticeHeaderService.selectById(inboundNoticeLine.getHeaderId());
        LOGGER.info("查询入库通知单头是否存在 inboundNoticeHeader.getId ： {} ", inboundNoticeHeader.getId());

        //查询当前仓库是否已经有该车的入库库存
        queryInboundStock(inboundNoticeLine.getLotNo1(),inboundNoticeHeader.getStoreHouseId());

        //得到可用储位(如果现场已经选择了库区，则根据库区选择有效库位)
        StoreLocation storeLocation;
        //获取车型属性值
        String vehicleType = "";
        if ("大".equals(vehicleSize)){
            vehicleType = "40";
        } else if("中".equals(vehicleSize)) {
            vehicleType = "50";
        } else if("小".equals(vehicleSize)) {
            vehicleType = "10";
        } else {
            vehicleType = "";
        }
        if(null != areaId){
            //南昌迅佳库 根据车型大中小关联库位类型 hgy 20200805
            if (inboundNoticeHeader.getStoreHouseId() == 10004) {
                if ("".equals(vehicleType)) {
                    storeLocation = storeLocationService.queryUsableLocationByAreaId(inboundNoticeHeader.getStoreHouseId(), areaId);
                } else {
                    //C库，暴库，暴库A，暴库B，暴库C不按照车型大小分配入库,只有A,B库区按照车型大小入库
                    if ("1082632938891288577".equals(Long.toString(areaId)) || "1082632984454012930".equals(Long.toString(areaId))) {
                        storeLocation = storeLocationService.getUsableLocationByAreaType(inboundNoticeHeader.getStoreHouseId(), areaId, vehicleType);
                    } else {
                        storeLocation = storeLocationService.queryUsableLocationByAreaId(inboundNoticeHeader.getStoreHouseId(), areaId);
                    }
                }
            } else {
                storeLocation = storeLocationService.queryUsableLocationByAreaId(inboundNoticeHeader.getStoreHouseId(), areaId);
            }
            if (Objects.isNull(storeLocation)) {
                throw new BaseException("无可用库位！");
            }

        } else {
            //南昌迅佳库 根据车型大中小关联库位类型 hgy 20200805
            if (inboundNoticeHeader.getStoreHouseId() == 10004) {
                if ("".equals(vehicleType)) {
                    storeLocation = storeLocationService.getUsableLocation(inboundNoticeHeader.getStoreHouseId());
                } else {
                    storeLocation = storeLocationService.getUsableLocationByType(inboundNoticeHeader.getStoreHouseId(), vehicleType);
                }
            } else {
                storeLocation = storeLocationService.getUsableLocation(inboundNoticeHeader.getStoreHouseId());
            }
            if (Objects.isNull(storeLocation)) {
                throw new BaseException("无可用库位！");
            }
        }
        LOGGER.info("仓库StoreHouseId :{} 得到可用库位为{}", inboundNoticeHeader.getStoreHouseId(), storeLocation.getName());

        User loginUser = userService.queryLoginUser(genMethod);

        //入库单头
        InboundPutawayHeader inboundPutawayHeader = new InboundPutawayHeader();
        BeanUtils.copyProperties(inboundNoticeHeader, inboundPutawayHeader);
        inboundPutawayHeader.setNoticeId(inboundNoticeHeader.getId());
        inboundPutawayHeader.setInboundNo(businessDocNumberService.getInboundPutAwayNo());
        inboundPutawayHeader.setId(snowFlakeId.nextId());
        inboundPutawayHeader.setType(inboundType);
        inboundPutawayHeader.setGenMethod(genMethod);
        inboundPutawayHeader.setStatus(Status.AUDIT);
        inboundPutawayHeader.setUserModified(loginUser.getName());
        inboundPutawayHeader.setUserCreate(loginUser.getName());
        inboundPutawayHeader.setInboundTime(new Date());
        inboundPutawayHeader.setGmtCreate(null); //创建时间、修改时间使用数据库自动处理
        inboundPutawayHeader.setGmtModified(null);

        //入库单明细
        InboundPutawayLine inboundPutawayLine = new InboundPutawayLine();
        BeanUtils.copyProperties(inboundNoticeLine, inboundPutawayLine);
        inboundPutawayLine.setHeaderId(inboundPutawayHeader.getId());
        inboundPutawayLine.setNoticeLineId(inboundNoticeLine.getId());
        inboundPutawayLine.setLocationId(storeLocation.getId());
        String areaName = storeLocation.getStoreAreaName();
        String locDetail = "";
        if (StringUtils.isNotBlank(areaName) && areaName.endsWith("区")) {
            locDetail = areaName.concat(storeLocation.getName());
        } else if (StringUtils.isNotBlank(areaName) && !areaName.endsWith("区")) {
            locDetail = areaName.concat("区").concat(storeLocation.getName());
        } else if (StringUtils.isBlank(areaName)) {
            locDetail = storeLocation.getName();
        }
        inboundPutawayLine.setLocationNo(locDetail);
        inboundPutawayLine.setInboundQty(inboundNoticeLine.getExpectQty());
        inboundPutawayLine.setInboundNetWeight(inboundNoticeLine.getExpectNetWeight());
        inboundPutawayLine.setInboundGrossWeight(inboundNoticeLine.getExpectGrossWeight());
        inboundPutawayLine.setInboundGrossCubage(inboundNoticeLine.getExpectGrossCubage());
        inboundPutawayLine.setInboundPackedCount(inboundNoticeLine.getExpectPackedCount());
        inboundPutawayLine.setId(snowFlakeId.nextId());
        inboundPutawayLine.setGmtCreate(null);//创建时间、修改时间使用数据库自动处理
        inboundPutawayLine.setGmtModified(null);
        inboundPutawayHeader.addInboundPutawayLine(inboundPutawayLine);

        //判断入库通知单notice_line_Id，是否存在入库作业记录，如存在则不能插入记录 hgy 20200828
        EntityWrapper<InboundPutawayLine> ew = new EntityWrapper<>();
        ew.eq("notice_line_id", inboundNoticeLine.getId());
        List<InboundPutawayLine> putawayLines = inboundPutawayLineService.selectList(ew);
        if (putawayLines.size()>0){
            throw new BaseException("入库失败，入库单LineId:"+inboundNoticeLine.getId()+"已存在!");
        }

        boolean result = insertMovementByPutaway(inboundPutawayHeader, loginUser) && savePutAway(inboundPutawayHeader);
        if (!result) {
            throw new BaseException("入库失败");
        }
        inboundNoticeHeaderService.updateStatus(inboundNoticeHeader.getId(),noticeLineId);

        // 确认入库后，自动完成前段的寻车、移车、提车任务以及收车质检任务(如果存在质检单)
        updateTaskFinish(inboundNoticeLine, loginUser);

        //2019-1-28 fix 增加重庆前置库1库,入库信息回传ERP(ERP入库信息-->工厂)

        Storehouse storehouse = storehouseService.selectById(inboundNoticeHeader.getStoreHouseId());

        // 仓库根据code 判断是否发送ERP,如后续调整基础数据时调整......
        if (storehouse != null && "重庆前置库1库".equals(storehouse.getCode())) {
            inboundERP(inboundNoticeLine,
                    inboundNoticeHeader,
                    loginUser,
                    inboundPutawayHeader,
                    inboundPutawayLine);
        }

        //如果是otm下发入库信息.入库成功,推送信息到OTM
        updateSendOTM(noticeLineId, inboundNoticeLine, loginUser);

        return inboundPutawayHeader;
    }


    //南昌提车段入库车辆进行发运操作
    private void inboundDispatch (String lineSourceKey, String lotNo1) {
        new Thread(() -> {
            try {
                //查询目的仓库是否南昌的仓库
                EntityWrapper<OtmOrderRelease> releaseEw = new EntityWrapper<>();
                releaseEw.eq("release_gid", lineSourceKey);
                releaseEw.eq("vin", lotNo1);
                List<OtmOrderRelease> otmOrderReleases = otmOrderReleaseMapper.selectHaveDispatch(releaseEw);
                if (CollectionUtils.isNotEmpty(otmOrderReleases)) {
                    OtmOrderRelease otmOrderRelease = otmOrderReleases.get(0);
                    OtmOrderRelease oor = new OtmOrderRelease();
                    oor.setId(otmOrderRelease.getId());
                    if(!otmOrderRelease.getStatus().equals(TableStatusEnum.STATUS_BS_INBOUND.getCode())){
                        oor.setStatus(TableStatusEnum.STATUS_BS_DISPATCH.getCode());
                    }
                    Calendar calendar = Calendar.getInstance();
                    calendar.setTime(new Date());
                    calendar.set(Calendar.HOUR, calendar.get(Calendar.HOUR) - 2);// 让日期减2
                    String shipDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(calendar.getTime());
                    oor.setShipDate(CommonMethod.stringToDate2(shipDate));
                    oor.setRealShipDate(new Date());
                    otmOrderReleaseMapper.updateById(oor);

                    //调整头表发运状态 增加判断明细都为已发运
                    EntityWrapper<OtmOrderRelease> oorEW = new EntityWrapper<>();
                    oorEW.eq("shipment_gid", otmOrderRelease.getShipmentGid());
                    oorEW.isNull("ship_date");
                    oorEW.ne("status", TableStatusEnum.STATUS_50.getCode());
                    List<OtmOrderRelease> notShipCount = otmOrderReleaseMapper.selectList(oorEW);
                    if (notShipCount.size() == 0) {
                        EntityWrapper<OtmShipment> otmShipmentEntityWrapper = new EntityWrapper<>();
                        otmShipmentEntityWrapper.eq("shipment_gid", otmOrderRelease.getShipmentGid());
                        otmShipmentEntityWrapper.ne("status", TableStatusEnum.STATUS_50.getCode());
                        OtmShipment os = new OtmShipment();
                        os.setStatus(TableStatusEnum.STATUS_BS_DISPATCH.getCode());
                        otmShipmentMapper.update(os, otmShipmentEntityWrapper);
                    }
                    OtmShipment otmShipment = new OtmShipment();
                    otmShipment.setShipmentGid(otmOrderRelease.getShipmentGid());
                    OtmShipment otmShipmentRes = otmShipmentMapper.selectOne(otmShipment);
                    //发运时间推送其他系统
                    iOrderReleaseService.pushInboundDispatch(otmOrderRelease, otmShipmentRes,shipDate);
                }
            } catch (Exception e) {
                LOGGER.error("入库自动发运异常:{}", e.getMessage());
            }
        }).start();
    }

    private void factoryPrepareToOff (Long storeHouseId, Long locationId, String lotNo1, Long putwayId) {
        try {
            EntityWrapper<FactoryPrepareInbound> factoryInboundEw = new EntityWrapper<>();
            factoryInboundEw.eq(CommonFields.VIN.getCode(), lotNo1);
            factoryInboundEw.eq(CommonFields.STORE_HOUSE_ID.getCode(), storeHouseId);
            factoryInboundEw.ne("prepare_status", TableStatusEnum.STATUS_50.getCode());
            List<FactoryPrepareInbound> factoryPrepareInbounds = factoryPrepareInboundMapper.selectList(factoryInboundEw);
            if (CollectionUtils.isNotEmpty(factoryPrepareInbounds)) {
                Sku querySku = new Sku();
                querySku.setLotNo1(lotNo1);
                Sku sku = skuMapper.selectOne(querySku);
                if (null != sku) {
                    Stock stock = new Stock();
                    stock.setStockProperty("备转正");
                    EntityWrapper<Stock> stockEntityWrapper = new EntityWrapper<>();
                    stockEntityWrapper.eq("sku_id", sku.getId());
                    stockEntityWrapper.eq("location_id", locationId);
                    stockEntityWrapper.eq(CommonFields.STORE_HOUSE_ID.getCode(), storeHouseId);
                    stockEntityWrapper.gt("qty", BigDecimal.ZERO);
                    stockMapper.update(stock, stockEntityWrapper);
                    List<Stock> stocks = stockMapper.selectList(stockEntityWrapper);

                    FactoryPrepareInbound updateParam = new FactoryPrepareInbound();
                    updateParam.setSkuId(sku.getId());
                    updateParam.setPutwayId(putwayId);
                    updateParam.setTargetStoreHouse(storeHouseId);
                    updateParam.setStockId(stocks.get(0).getId());
                    updateParam.setId(factoryPrepareInbounds.get(0).getId());
                    factoryPrepareInboundMapper.updateById(updateParam);
                }
            }
        } catch (Exception e) {
            LOGGER.error("更新库存属性异常{}", e.getMessage());
        }
    }

    /**
     * 查询当前仓库是否已有该车的入库库存
     *
     * @param lotNo1       车架号
     * @param storeHouseId 仓库id
     */
    private void queryInboundStock (String lotNo1, Long storeHouseId) {
        LOGGER.info("查询 lotNo1 :{} 当前仓库storeHouseId {} 是否已有该车的入库库存", lotNo1, storeHouseId);
        Sku param = new Sku();
        param.setLotNo1(lotNo1);
        Sku sku = skuMapper.selectOne(param);
        if (null == sku) {
            return;
        }
        EntityWrapper<Stock> stockEW = new EntityWrapper<>();
        stockEW.eq("sku_id", sku.getId());
        stockEW.ge("qty", BigDecimal.ONE);
        stockEW.eq("store_house_id",storeHouseId);
        List<Stock> stock = stockMapper.selectList(stockEW);
        if (CollectionUtils.isNotEmpty(stock)) {
            throw new BaseException("该车在该仓库已有库存信息，请重新确认");
        }
    }

    //重庆前置库1库 传送ERP
    private void inboundERP (InboundNoticeLine inboundNoticeLine,
                             InboundNoticeHeader inboundNoticeHeader,
                             User loginUser,
                             InboundPutawayHeader inboundPutawayHeader,
                             InboundPutawayLine inboundPutawayLine) {
        new Thread(() -> {
            String url = properties.getFetchInboundFromTMSUrl() + InterfaceAddrEnum.INBOUND_ERP.getAddress();
            Integer timeOut = properties.getSocketTimeOut();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String encode_key = "ZL12345";                      //简单的ERP接口鉴权(头携带key)
            Map<String, Object> map = new HashMap<>();
            Map<String, Object> headerMap = new HashMap<>();
            headerMap.put("encode-key", encode_key);
            map.put("vin", inboundNoticeLine.getLotNo1());
            map.put("custshipno", inboundNoticeLine.getOwnerOrderNo());
            map.put("dt_inware", sdf.format(inboundPutawayHeader.getInboundTime()));
            map.put("userno", loginUser.getName());
            map.put("warehouse", "重庆前置库");
            map.put("pos", inboundPutawayLine.getLocationNo());
            EntityWrapper<ExceptionRegister> erEW = new EntityWrapper<>();
            erEW.eq("vin", inboundNoticeLine.getLotNo1())
                    .ne("status", TableStatusEnum.STATUS_0.getCode())
                    .ne("task_node", TableStatusEnum.STATUS_50.getCode());//非出库节点
            int count = registerService.selectCount(erEW);
            if (count > 0) {
                map.put("type", TableStatusEnum.STATUS_2.getCode());
            } else {
                map.put("type", TableStatusEnum.STATUS_1.getCode());
            }
            String erpParam = JSONObject.toJSONString(map);
            //日志记录
            ItfExplogHeader exH = getItfExplogHeader(inboundNoticeHeader);

            ItfExplogLine exl = getItfExplogLine(inboundNoticeLine, url, erpParam, exH);
            if (LOGGER.isInfoEnabled()) {
                LOGGER.info("入库确认连接ERP url:{},param:{}", url, erpParam);
            }
            String erpRes;
            try {
                erpRes = HttpRequestUtil.sendHttpPost(url, headerMap, map, timeOut);
                if (LOGGER.isInfoEnabled()) {
                    LOGGER.info("入库确认连接ERP url:{},result:{}", url, erpRes);
                }
            } catch (Exception e) {
                insertLog(exH, exl, TableStatusEnum.STATUS_N);
                LOGGER.error("入库确认连接ERP url:{},param:{}  超时异常:{}", url, erpParam, e);
                throw new BaseException("入库连接ERP异常");
            }
            if (StringUtils.isNotBlank(erpRes)) {
                JSONObject jsonObject = JSONObject.parseObject(erpRes);
                Boolean success = jsonObject.getBoolean("success");
                if (!success) {
                    String message = jsonObject.getString("message");
                    insertLog(exH, exl, TableStatusEnum.STATUS_N);
                    throw new BaseException("入库同步ERP失败," + message);
                } else {
                    insertLog(exH, exl, TableStatusEnum.STATUS_Y);
                }
            } else {
                insertLog(exH, exl, TableStatusEnum.STATUS_N);
                throw new BaseException("入库同步ERP失败,响应结果为空");
            }
        }).start();
    }

    private ItfExplogLine getItfExplogLine(InboundNoticeLine inboundNoticeLine, String url, String erpParam, ItfExplogHeader exH) {
        ItfExplogLine exl = new ItfExplogLine();
        exl.setId(snowFlakeId.nextId());
        exl.setHeaderId(exH.getId());
        exl.setExportType("CQ入库确认传ERP");
        exl.setRelationId(inboundNoticeLine.getId());
        exl.setTargetSource("ERP-TMS-IN");
        exl.setInterfaceUrl(url);
        exl.setExportStartTime(new Date());
        exl.setExportRemarks("CQ入库确认传ERP");
        exl.setDataContent(erpParam);
        ArrayList<ItfExplogLine> lines = Lists.newArrayList();
        lines.add(exl);
        exH.setItfExplogLines(lines);
        return exl;
    }

    private ItfExplogHeader getItfExplogHeader(InboundNoticeHeader inboundNoticeHeader) {
        ItfExplogHeader exH = new ItfExplogHeader();
        exH.setId(snowFlakeId.nextId());
        exH.setExportType("CQ入库确认传ERP");
        exH.setRelationId(inboundNoticeHeader.getId());
        exH.setTargetSource("ERP-TMS-IN");
        exH.setExportRemarks("CQ入库确认传ERP");
        return exH;
    }

    private void insertLog(ItfExplogHeader exH, ItfExplogLine exl, TableStatusEnum status1) {
        exl.setExportStatus(status1.getCode());
        exH.setExportStatus(status1.getCode());
        //记录wms日志
        new Thread(() -> {
            explogHeaderService.insertWithLines(exH);
        }).start();
    }

    private void updateSendOTM (Long noticeLineId, InboundNoticeLine inboundNoticeLine, User loginUser) {
        String key = inboundNoticeLine.getLineSourceKey();
        if (StringUtils.isNotEmpty(key)) {
            EntityWrapper<OtmOrderRelease> ew = new EntityWrapper<>();
            ew.eq("release_gid", key)
                    .ne("status", TableStatusEnum.STATUS_50.getCode());
            List<OtmOrderRelease> otmOrderReleases = releaseService.selectList(ew);
            if (CollectionUtils.isNotEmpty(otmOrderReleases)) {
                if (otmOrderReleases.size() > 1) {
                    throw new BaseException("通知明细存在多条ReleaseGid:" + key);
                }
                OtmOrderRelease release = otmOrderReleases.get(0);
                //调整release 状态为已入库
                setReleaseAndStatusLog(loginUser, release);

                EntityWrapper<ItfExplogLine> wifEw = new EntityWrapper<>();
                wifEw.eq("relation_id", noticeLineId);
                wifEw.eq("export_type", InterfaceEventEnum.BS_WMS_IN.getCode());
                List<ItfExplogLine> itfExplogLines = logLineService.selectList(wifEw);
                if (CollectionUtils.isEmpty(itfExplogLines)) {
                    OTMEvent event = integrationService.getOtmEvent(String.valueOf(noticeLineId), key, InterfaceEventEnum.BS_WMS_IN.getCode(), release.getShipmentGid(),"入库回传OTM");
                    String res = nodeExport.exportEventToOTM(event);
                    try {
                        integrationService.insertExportLog( String.valueOf(noticeLineId), event, res, "入库回传OTM", InterfaceEventEnum.BS_WMS_IN.getCode());
                    } catch (Exception e) {
                        LOGGER.error("入库推送OTM失败:{}", e);
                        throw new BaseException("入库推送OTM失败");
                    }
                } else {
                    OTMEvent event = integrationService.getOtmEvent(String.valueOf(noticeLineId), key, InterfaceEventEnum.BS_WMS_IN.getCode(), release.getShipmentGid(),"入库回传OTM");
                    String res = nodeExport.exportEventToOTM(event);
                    ItfExplogLine logLine = new ItfExplogLine();
                    logLine.setId(itfExplogLines.get(0).getId());
                    logLine.setDataContent(JSONObject.toJSONString(event));
                    logLine.setRequestId(res);
                    itfExplogLineMapper.updateById(logLine);
                }
            }
        }
    }

    private void setReleaseAndStatusLog(User loginUser, OtmOrderRelease release) {
        OtmOrderRelease oor = new OtmOrderRelease();
        oor.setStatus(TableStatusEnum.STATUS_BS_INBOUND.getCode());
        oor.setId(release.getId());
        releaseService.updateById(oor);
        StatusLog statusLog = new StatusLog();
        statusLog.setTableType(TableStatusEnum.STATUS_10.getCode());
        statusLog.setTableId(String.valueOf(release.getId()));
        statusLog.setStatus(TableStatusEnum.STATUS_BS_INBOUND.getCode());
        statusLog.setStatusName(TableStatusEnum.STATUS_BS_INBOUND.getDetail());
        statusLog.setUserCreate(loginUser.getName());
        statusLogService.insert(statusLog);
    }

    private void updateTaskFinish (InboundNoticeLine inboundNoticeLine, User loginUser) {
        new Thread(() -> {
            EntityWrapper<OpTask> taskEW = new EntityWrapper<>();
            taskEW.eq("waybill_no", inboundNoticeLine.getLineSourceKey())
                    .in("status", new String[]{TableStatusEnum.STATUS_10.getCode(),
                            TableStatusEnum.STATUS_20.getCode()});
            OpTask task = new OpTask();
            task.setStatus(TableStatusEnum.STATUS_30.getCode());
            task.setStartTime(new Date());
            task.setFinishTime(new Date());
            task.setUserModified(loginUser.getName());
            taskService.update(task, taskEW);
            //质检
            EntityWrapper<InboundInspectLine> inspectLineEW = new EntityWrapper<>();
            inspectLineEW.eq("notice_line_id", inboundNoticeLine.getId())
                    .eq("status", TableStatusEnum.STATUS_0.getCode());
            InboundInspectLine line = new InboundInspectLine();
            line.setStatus(TableStatusEnum.STATUS_10.getCode());
            line.setRemarks("入库质检合格");
            line.setDealMethod(TableStatusEnum.STATUS_10.getCode());
            inspectLineService.update(line, inspectLineEW);

            EntityWrapper<InboundInspectHeader> headEW = new EntityWrapper<>();
            headEW.eq("notice_id", inboundNoticeLine.getHeaderId())
                    .eq("status", TableStatusEnum.STATUS_0.getCode());
            InboundInspectHeader inspectHeader = new InboundInspectHeader();
            inspectHeader.setStatus(TableStatusEnum.STATUS_10.getCode());
            inspectHeader.setOrderDate(new Date());
            inspectHeader.setInspector(loginUser.getName());
            inspectHeader.setQualifiedSumQty(BigDecimal.ONE);
            inspectHeader.setIsFinish(1);
            inspectHeaderService.update(inspectHeader, headEW);
        }).start();
    }


    @Override
    public boolean savePutAway (InboundPutawayHeader inboundPutawayHeader) {
        List<InboundPutawayLine> inboundPutawayLines = inboundPutawayHeader.getInboundPutawayLineList();
        if (CollectionUtils.isEmpty(inboundPutawayLines)) {
            return false;
        }
        return insert(inboundPutawayHeader) && insertputawayLineData(inboundPutawayLines);
    }

    private boolean insertputawayLineData (List<InboundPutawayLine> inboundPutawayLines) {
        try {
            for (InboundPutawayLine inboundPutawayLine : inboundPutawayLines) {
                inboundPutawayLineService.insert(inboundPutawayLine);
            }
            return true;
        } catch (Exception e) {
            LOGGER.error("保存入库记录明细异常", e.getMessage());
            return false;
        }
    }

    /**
     * 根据入库单插入移位单
     */
    @Override
    public boolean insertMovementByPutaway(InboundPutawayHeader inboundPutawayHeader, User loginUser) {
        //移位单头
        MovementHeader movementHeader = new MovementHeader();
        movementHeader.setMovementNo(businessDocNumberService.getMovementNo());
        movementHeader.setOwnerId(inboundPutawayHeader.getOwnerId());
        movementHeader.setStoreHouseId(inboundPutawayHeader.getStoreHouseId());
        movementHeader.setOrderDate(inboundPutawayHeader.getInboundTime());
        movementHeader.setBusinessType(MovementType.INBOUND_MOVE);
        movementHeader.setBusinessDocId(inboundPutawayHeader.getId());
        movementHeader.setUserModified(loginUser.getName());
        movementHeader.setUserCreate(loginUser.getName());
        for (InboundPutawayLine inboundPutawayLine : inboundPutawayHeader.getInboundPutawayLineList()) {
            MovementLine movementLine = new MovementLine();
            BeanUtils.copyProperties(inboundPutawayLine, movementLine);
            movementLine.setHeaderId(movementHeader.getId());
            movementLine.setRelationLineId(inboundPutawayLine.getId());
            movementLine.setType(MovementType.LINE_INTO);
            movementLine.setDestinationLocationId(inboundPutawayLine.getLocationId());
            movementLine.setQty(inboundPutawayLine.getInboundQty());
            movementLine.setNetWeight(inboundPutawayLine.getInboundNetWeight());
            movementLine.setGrossWeight(inboundPutawayLine.getInboundGrossWeight());
            movementLine.setGrossCubage(inboundPutawayLine.getInboundGrossCubage());
            movementLine.setPackedCount(inboundPutawayLine.getInboundPackedCount());
            movementHeader.addMovementLine(movementLine);
        }
        //保存并审核移位单、入库单
        return movementHeaderService.createAndAuditMovenment(movementHeader);
    }

    /**
     * 查询OTM是否有指令信息
     *
     * @param shipmentXid 指令号
     */
    private boolean queryShipmentStatusByOtm (String shipmentXid) {
        try {
            //组装数据给OTM
            Map<String, String> param = new HashMap<>();
            param.put("shipmentXid", shipmentXid);
            String jsonParam = JSONObject.toJSONString(param);
            String result = HttpClientUtil.postJson(properties.getOtmhost() + InterfaceAddrEnum.QUERYISSHIPMENT_OTM.getAddress(), null, jsonParam,
                    properties.getSocketTimeOut());
            LOGGER.info("查询指令状态 result:{},{}", result, shipmentXid);
            if (StringUtils.isNotBlank(result)) {
                JSONObject jsonObject = JSONObject.parseObject(result);
                if (jsonObject.getString("success").equals("false") && jsonObject.getString("data").equals(TableStatusEnum.STATUS_N.getCode())) {
                    return false;
                }
            }
        } catch (Exception e) {
            LOGGER.error("otm查询指令状态异常！" + e.getMessage());
        }
        return true;
    }

    //南昌发运库1库 增加手工库位与车型大小属性是否匹配判断 hgy 20200806
    @Transactional(rollbackFor = Exception.class)
    public Long updateNoticeLineWithLocationId (Long noticeLineId, String inboundType, String genMethod, Long storeLoactionId) {

        if (Objects.isNull(storeLoactionId)) throw new BaseException("未获取到选中库位");
        //获取入库通知单明细
        InboundNoticeLine inboundNoticeLine = inboundNoticeLineService.selectById(noticeLineId);
        if (Objects.isNull(inboundNoticeLine)) throw new BaseException("入库通知单明细不存在！");
        if (inboundNoticeLine.getStatus().equals(Status.Inbound.ALL)) throw new BaseException("已入库不能重复入库！");
        if (inboundNoticeLine.getStatus().equals(Status.Inbound.CANCEL)) throw new BaseException("入库已取消，不能入库！");



        //指令是否被取消
        try {
            if (StringUtils.isNotBlank(inboundNoticeLine.getLineSourceKey())) {
                String shipmentXid = inboundNoticeHeaderMapper.selectSourceKeyByLineId(inboundNoticeLine.getId(), inboundNoticeLine.getLineSourceKey());
                if (StringUtils.isNotBlank(shipmentXid) && !queryShipmentStatusByOtm(shipmentXid)) {
                    iOtmShipmentService.deleteShipmentInfo(shipmentXid);
                    throw new BaseException("车架号" + inboundNoticeLine.getLotNo1() + "和ILS系统数据不一致，已调整！");
                }
            }
        } catch (Exception e) {
            LOGGER.error("OTM查询指令状态信息异常{}", e.getMessage());
        }

        //工厂未备料数据是否有未出库的车辆
        EntityWrapper<FactoryPrepareInbound> fpiEw = new EntityWrapper<>();
        fpiEw.eq(CommonFields.VIN.getCode(), inboundNoticeLine.getLotNo1());
        fpiEw.eq("prepare_status", TableStatusEnum.STATUS_10.getCode());
        List<FactoryPrepareInbound> fpiList = factoryPrepareInboundMapper.selectList(fpiEw);
        if (CollectionUtils.isNotEmpty(fpiList)) {
            throw new BaseException("车架号" + inboundNoticeLine.getLotNo1() + "有工厂未备料入库的数据还未出库！");
        }

        //获取登录用户
        User loginUser = userService.queryLoginUser(genMethod);

        //获取入库通知单头
        InboundNoticeHeader inboundNoticeHeader =
                inboundNoticeHeaderService.selectById(inboundNoticeLine.getHeaderId());

        //只是针对南昌发运库1库，判断入库库位类型与车型大小是否匹配 hgy 20200806
        String vehicleType = "";
        String vehicleSize = "";
        vehicleSize = inboundNoticeLine.getLotNo5();
        if ("大".equals(vehicleSize)){
            vehicleType = "40";
        } else if("中".equals(vehicleSize)) {
            vehicleType = "50";
        } else if("小".equals(vehicleSize)) {
            vehicleType = "10";
        } else {
            vehicleType = "";
        }
        StoreLocation storeLocation1 = null;
        String locationType = "";
        String locaitonTypeDesc = "";
        storeLocation1 = storeLocationService.selectById(storeLoactionId);
        if (!Objects.isNull(storeLocation1)){
            locationType = storeLocation1.getType();
            if ("10".equals(locationType)){
                locaitonTypeDesc = "普通库位";
            } else if("20".equals(locationType)) {
                locaitonTypeDesc = "临时库位";
            } else if("30".equals(locationType)) {
                locaitonTypeDesc = "虚拟库位";
            } else if("40".equals(locationType)) {
                locaitonTypeDesc = "大库位";
            } else if("50".equals(locationType)) {
                locaitonTypeDesc = "中库位";
            } else {
                locaitonTypeDesc = "";
            }
            if ((storeLocation1.getStoreHouseId() == 10004) && (!"".equals(vehicleType))) {
                if ("10".equals(locationType) || "40".equals(locationType) || "50".equals(locationType)) {
                    if (!vehicleType.equals(locationType))
                        throw new BaseException("待分配的库位类型与车型不匹配，车型大小是：" + vehicleSize + ",库位类型是：" + locaitonTypeDesc);
                }
            }
        }

        //得到可用储位
        List<StoreLocation> storeLocations = storeLocationService.
                listUsableLocation(inboundNoticeHeader.getStoreHouseId());
        if (CollectionUtils.isEmpty(storeLocations)) throw new BaseException("无可用仓储库位！");
        StoreLocation storeLocation = null;
        for (StoreLocation storeLocationItem : storeLocations) {
            if (storeLocationItem.getId().equals(storeLoactionId)) storeLocation = storeLocationItem;
        }
        if (Objects.isNull(storeLocation)) throw new BaseException("该库位不可用！");

        //入库单头
        InboundPutawayHeader inboundPutawayHeader = new InboundPutawayHeader();
        BeanUtils.copyProperties(inboundNoticeHeader, inboundPutawayHeader);
        inboundPutawayHeader.setNoticeId(inboundNoticeHeader.getId());
        inboundPutawayHeader.setInboundNo(businessDocNumberService.getInboundPutAwayNo());
        inboundPutawayHeader.setId(snowFlakeId.nextId());
        inboundPutawayHeader.setType(inboundType);
        inboundPutawayHeader.setGenMethod(genMethod);
        inboundPutawayHeader.setStatus(Status.AUDIT);
        inboundPutawayHeader.setInboundTime(new Date());
        inboundPutawayHeader.setUserCreate(loginUser.getName());
        inboundNoticeHeader.setUserModified(loginUser.getName());
        inboundPutawayHeader.setGmtCreate(null); //创建时间、修改时间使用数据库自动处理
        inboundPutawayHeader.setGmtModified(null);

        //入库单明细
        InboundPutawayLine inboundPutawayLine = new InboundPutawayLine();
        BeanUtils.copyProperties(inboundNoticeLine, inboundPutawayLine);
        inboundPutawayLine.setHeaderId(inboundPutawayHeader.getId());
        inboundPutawayLine.setNoticeLineId(inboundNoticeLine.getId());
        inboundPutawayLine.setLocationId(storeLocation.getId());
        String areaName = storeLocation.getStoreAreaName();

        String locDetail = storeLocationService.queryAreaCodeDetail(areaName, storeLocation.getName());
        if (StringUtils.isBlank(locDetail)) {
            locDetail = "";
        }

        inboundPutawayLine.setLocationNo(locDetail);
        inboundPutawayLine.setInboundQty(inboundNoticeLine.getExpectQty());
        inboundPutawayLine.setInboundNetWeight(inboundNoticeLine.getExpectNetWeight());
        inboundPutawayLine.setInboundGrossWeight(inboundNoticeLine.getExpectGrossWeight());
        inboundPutawayLine.setInboundGrossCubage(inboundNoticeLine.getExpectGrossCubage());
        inboundPutawayLine.setInboundPackedCount(inboundNoticeLine.getExpectPackedCount());
        inboundPutawayLine.setId(snowFlakeId.nextId());
        inboundPutawayLine.setGmtCreate(null);//创建时间、修改时间使用数据库自动处理
        inboundPutawayLine.setGmtModified(null);
        inboundPutawayHeader.addInboundPutawayLine(inboundPutawayLine);

        boolean result = insertMovementByPutaway(inboundPutawayHeader, loginUser) && savePutAway(inboundPutawayHeader);
        if (!result) {
            throw new BaseException("入库失败");
        }
        inboundNoticeHeaderService.updateStatus(inboundNoticeHeader.getId(), noticeLineId);

        // 确认入库后，自动完成前段的寻车、移车、提车任务以及收车质检任务(如果存在质检单)
        updateTaskFinish(inboundNoticeLine, loginUser);

        //2019-1-28 fix 增加重庆前置库1库,入库信息回传ERP(ERP入库信息-->工厂)
        Storehouse storehouse = storehouseService.selectById(inboundNoticeHeader.getStoreHouseId());

        // 仓库根据code 判断是否发送ERP,如后续调整基础数据时调整......
        if (storehouse != null && "重庆前置库1库".equals(storehouse.getCode())) {
            inboundERP(inboundNoticeLine,
                    inboundNoticeHeader,
                    loginUser,
                    inboundPutawayHeader,
                    inboundPutawayLine);
        }

        //如果是otm下发入库信息.入库成功,推送信息到OTM
        updateSendOTM(noticeLineId, inboundNoticeLine, loginUser);

        return inboundPutawayLine.getId();
    }

    @Override
    public void pushInboundToOtm (Long inboundId, InboundNoticeLine inboundNoticeLine, String pushMessage) {
        User user = new User();
        user.setName("admin");
        this.updateSendOTM(inboundId, inboundNoticeLine, user);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public synchronized InboundPutawayHeader inboundNoticeLineId (Long noticeLineId, String inboundType, String genMethod, Long areaId, Long locationId) {
        LOGGER.info("入库通知单noticeLineId : {}进行入库操作", noticeLineId);
        //获取入库通知单明细
        InboundNoticeLine inboundNoticeLine = inboundNoticeLineService.selectById(noticeLineId);
        if (Objects.isNull(inboundNoticeLine)) {
            throw new BaseException("入库通知单明细不存在！");
        }
        if (inboundNoticeLine.getStatus().equals(Status.Inbound.ALL)) {
            throw new BaseException("已入库不能重复入库！");
        }
        if (inboundNoticeLine.getStatus().equals(Status.Inbound.CANCEL)) {
            throw new BaseException("入库已取消，不能入库！");
        }
        //获取入库通知单头
        InboundNoticeHeader inboundNoticeHeader = inboundNoticeHeaderService.selectById(inboundNoticeLine.getHeaderId());
        LOGGER.info("查询入库通知单头是否存在 inboundNoticeHeader.getId ： {} ", inboundNoticeHeader.getId());

        //查询当前仓库是否已经已经有入库库存
        queryInboundStock(inboundNoticeLine.getLotNo1(), inboundNoticeHeader.getStoreHouseId());
        //得到可用储位(如果现场已经选择了库区，则根据库区选择有效库位)
        StoreLocation storeLocation = storeLocationService.selectById(locationId);
        if (null == storeLocation) {
            throw new BaseException("无可用库位！");
        }
        LOGGER.info("仓库StoreHouseId :{} 得到可用库位为{}", inboundNoticeHeader.getStoreHouseId(), storeLocation.getName());

        //查询用户登录信息
        User loginUser = userService.queryLoginUser(genMethod);

        //入库单头
        InboundPutawayHeader inboundPutawayHeader = new InboundPutawayHeader();
        BeanUtils.copyProperties(inboundNoticeHeader, inboundPutawayHeader);
        inboundPutawayHeader.setNoticeId(inboundNoticeHeader.getId());
        inboundPutawayHeader.setInboundNo(businessDocNumberService.getInboundPutAwayNo());
        inboundPutawayHeader.setId(snowFlakeId.nextId());
        inboundPutawayHeader.setType(inboundType);
        inboundPutawayHeader.setGenMethod(genMethod);
        inboundPutawayHeader.setStatus(Status.AUDIT);
        inboundPutawayHeader.setUserModified(loginUser.getName());
        inboundPutawayHeader.setUserCreate(loginUser.getName());
        inboundPutawayHeader.setInboundTime(new Date());

        //入库单明细
        InboundPutawayLine inboundPutawayLine = new InboundPutawayLine();
        BeanUtils.copyProperties(inboundNoticeLine, inboundPutawayLine);
        inboundPutawayLine.setHeaderId(inboundPutawayHeader.getId());
        inboundPutawayLine.setNoticeLineId(inboundNoticeLine.getId());
        inboundPutawayLine.setLocationId(storeLocation.getId());
        String areaName = storeLocation.getStoreAreaName();

        String locDetail = storeLocationService.queryAreaCodeDetail(areaName,storeLocation.getName());
        if(StringUtils.isBlank(locDetail)){
            locDetail = "";
        }

        inboundPutawayLine.setLocationNo(locDetail);
        inboundPutawayLine.setInboundQty(inboundNoticeLine.getExpectQty());
        inboundPutawayLine.setInboundNetWeight(inboundNoticeLine.getExpectNetWeight());
        inboundPutawayLine.setInboundGrossWeight(inboundNoticeLine.getExpectGrossWeight());
        inboundPutawayLine.setInboundGrossCubage(inboundNoticeLine.getExpectGrossCubage());
        inboundPutawayLine.setInboundPackedCount(inboundNoticeLine.getExpectPackedCount());
        inboundPutawayLine.setId(snowFlakeId.nextId());
        inboundPutawayHeader.addInboundPutawayLine(inboundPutawayLine);

        boolean result = insertMovementByPutaway(inboundPutawayHeader, loginUser) && savePutAway(inboundPutawayHeader);
        if (!result) {
            throw new BaseException("入库失败");
        }
        inboundNoticeHeaderService.updateStatus(inboundNoticeHeader.getId(), noticeLineId);

        // 确认入库后，自动完成前段的寻车、移车、提车任务以及收车质检任务(如果存在质检单)
        updateTaskFinish(inboundNoticeLine, loginUser);

        Storehouse storehouse = storehouseService.selectById(inboundNoticeHeader.getStoreHouseId());

        //是否工厂未备料入库转正操作
        factoryPrepareToOff(storehouse.getId(), inboundPutawayLine.getLocationId(), inboundNoticeLine.getLotNo1(),inboundPutawayLine.getId());

        //如果是otm下发入库信息.入库成功,推送信息到OTM
        updateSendOTM(noticeLineId, inboundNoticeLine, loginUser);
        return inboundPutawayHeader;
    }

    /**
     * 提示目标库位
     *
     * @param headerId
     * @param vin
     * @return
     */
    public String getRealDestAddress(Long headerId, String vin) {
        InboundNoticeHeader inboundNoticeHeader = inboundNoticeHeaderService.selectById(headerId);
        if (null != inboundNoticeHeader) {
            if (null != inboundNoticeHeader) {
                OtmOrderRelease param = new OtmOrderRelease();
                param.setShipmentGid(inboundNoticeHeader.getSourceKey());
                param.setVin(vin);
                OtmOrderRelease otmOrderRelease = otmOrderReleaseMapper.selectOne(param);
                if (Objects.nonNull(otmOrderRelease)) {
                    if (StringUtils.isNotEmpty(otmOrderRelease.getDestLocationGid())) {
                        return ",请确认入库地址是否是：" + otmOrderRelease.getDestLocationGid() + "【" + otmOrderRelease.getDestLocationName() + "】";
                    }
                }
            }
        }
        return "";
    }
}
